package visualization;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import optimizers.ganeat.Individual;
import optimizers.ganeat.Species;

import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import gui.ExportEvolutionToImagesTool;
import gui.PopupIndivInfo;

public class GraphDisplayer extends JPanel {
	protected int nPanel;
	protected GridLayout grid;
	protected JPanel panel;
	protected JScrollPane scrollBar;
	private ArrayList<ArrayList<Species>> data;

	public GraphDisplayer(int w, int h) {
		super();
		panel = new JPanel();
		grid = new GridLayout(0, 1);
		scrollBar = new JScrollPane(panel,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

		this.add(scrollBar);
		panel.setLayout(grid);
		grid.setColumns(1);
		// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setPreferredSize(new Dimension(w, h));
		nPanel = 0;
	}

	public void display() {
		this.getContentPanel().revalidate();
		this.getContentPanel().setVisible(true);
	}

	public JScrollPane getContentPanel() {
		return scrollBar;
	}

	public void addPrunningTest(BasicVisualizationServer vv1,
			BasicVisualizationServer vv2) {
		JPanel newPanel = new JPanel();
		nPanel++;

		if (nPanel % 2 == 1) {
			newPanel.setBackground(Color.GRAY);
		} else {
			newPanel.setBackground(Color.darkGray);
		}
		FlowLayout flow = new FlowLayout();
		newPanel.setLayout(flow);
		newPanel.add(vv1);
		newPanel.add(vv2);
		this.panel.add(newPanel);
	}

	public void addGraph(BasicVisualizationServer vv, JPanel timeplotPanel,
			JPanel info) {
		addGraph(vv, timeplotPanel, info, null, null);
	}

	public void addGraph(Individual indiv, JPanel timeplotPanel, JPanel info) {
		JPanel newPanel = new JPanel();
		nPanel++;

		Color color;
		if (nPanel % 2 == 1) {
			color = Color.GRAY;
		} else {
			color = Color.DARK_GRAY;
		}
		FlowLayout flow = new FlowLayout();
		newPanel.setLayout(flow);

		JPanel leftSide = new JPanel();
		leftSide.setLayout(new BoxLayout(leftSide, BoxLayout.Y_AXIS));
		leftSide.add(indivPanel(indiv, color));

		newPanel.add(leftSide);
		if (timeplotPanel != null) {
			newPanel.add(timeplotPanel);
		}
		if (info != null)
			newPanel.add(info);

		this.panel.add(newPanel);
	}

	public JPanel indivPanel(Individual indiv, Color backGround) {
		JPanel newPanel = new JPanel();
		newPanel.setBackground(backGround);
		FlowLayout flow = new FlowLayout();
		newPanel.setLayout(flow);

		JPanel leftSide = new JPanel();
		leftSide.setLayout(new BoxLayout(leftSide, BoxLayout.Y_AXIS));
		final VisualizationViewer<String, String> vv = (VisualizationViewer<String, String>) indiv
				.getOligoModel().getVisualization(250, 250);

		leftSide.add(vv);
		JButton button = new JButton("Evolution Detail");
		button.addActionListener(new IndividualActionListioner(indiv) {
		});
		JButton button1 = new JButton("Evolution Visual");
		button1.addActionListener(new IndividualActionListionerVisual(indiv) {
		});
		leftSide.add(button);
		leftSide.add(button1);
		newPanel.add(leftSide);
		JPanel timeplotPanel = indiv.displayTimeSeriesWithButton();
		if (timeplotPanel != null) {
			newPanel.add(timeplotPanel);
		}
		JPanel info = indiv.printModelInTextArea();
		if (info != null) {
			JButton button2 = new JButton("Model Information");
			button2.addActionListener(new IndividualActionListionerText(indiv) {
			});
			leftSide.add(button2);
		}
		final String indivString = indiv.toString();
		JButton button3 = new JButton("Save");
		button3.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				JFileChooser fc = new JFileChooser();
				int returnVal;
				do {
					returnVal = fc.showSaveDialog(null);
				} while (returnVal != JFileChooser.APPROVE_OPTION
						&& returnVal != JFileChooser.CANCEL_OPTION);
				File file = fc.getSelectedFile();
				if (file != null) {
					try {
						PrintWriter out = new PrintWriter(file);
						out.println(indivString);
						out.close();
						JOptionPane.showMessageDialog(common.Static.frame,
								"Individual Saved.");
					} catch (FileNotFoundException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}

			}
		});
		leftSide.add(button3);

		JButton button4 = new JButton("Save As Image");
		button4.addActionListener(new ExportActionListener(
				(VisualizationViewer<String, String>) vv));
		leftSide.add(button4);

		JButton button5 = new JButton("Export Evolution");
		button5.addActionListener(new IndividualActionListionerExport(indiv));
		leftSide.add(button5);
		return newPanel;
	}

	public JPanel addIndividual(Individual indiv) {
		nPanel++;
		JPanel result = null;
		if (nPanel % 2 == 1) {
			result = indivPanel(indiv, Color.GRAY);
		} else {
			result = indivPanel(indiv, Color.DARK_GRAY);
		}
		this.panel.add(result, 0);
		return result;
	}

	public void addGraph(BasicVisualizationServer vv, JPanel timeplotPanel,
			JPanel info, Individual parent1, Individual parent2) {
		JPanel newPanel = new JPanel();
		nPanel++;

		if (nPanel % 2 == 1) {
			newPanel.setBackground(Color.GRAY);
		} else {
			newPanel.setBackground(Color.darkGray);
		}
		FlowLayout flow = new FlowLayout();
		newPanel.setLayout(flow);

		JPanel leftSide = new JPanel();
		leftSide.setLayout(new BoxLayout(leftSide, BoxLayout.Y_AXIS));
		leftSide.add(vv);

		newPanel.add(leftSide);
		if (timeplotPanel != null) {
			newPanel.add(timeplotPanel);
		}
		if (info != null)
			newPanel.add(info);

		this.panel.add(newPanel);
	}

	public void addSpecies(Species sp) {
		Individual bestIndiv = sp.getBestIndividual();
		JPanel newPanel = new JPanel();
		nPanel++;
		Color color;
		if (nPanel % 2 == 1) {
			color = Color.GRAY;
		} else {
			color = Color.DARK_GRAY;
		}
		this.panel.add(newPanel);
		newPanel.setLayout(new BoxLayout(newPanel, BoxLayout.Y_AXIS));
		newPanel.add(indivPanel(sp.getBestIndividual(), color));

		JTextPane txtPane = new JTextPane();
		String text = sp.toString();
		txtPane.setText(text);
		newPanel.add(txtPane);

		JPanel lower = null;
		lower = new JPanel();
		newPanel.add(lower);
		lower.add(sp.representative.getOligoModel().getVisualization(120, 120));
		Individual parent = sp.representative.parent1;
		/*
		 * while(parent != null){ newPanel.add(indivPanel(parent, color));
		 * parent = parent.parent1; }
		 */
		for (int i = 0; i < Math.min(9, sp.individuals.size()); i++) {
			lower.add(sp.individuals.get(i).getOligoModel()
					.getVisualization(120, 120));
		}
		for (int i = 9; i < sp.individuals.size(); i++) {
			if (i % 10 == 9) {
				lower = new JPanel();
				newPanel.add(lower);
			}
			lower.add(sp.individuals.get(i).getOligoModel()
					.getVisualization(120, 120));
		}
		this.panel.add(newPanel);
	}

	public void addGraphWithPrune(BasicVisualizationServer vv1,
			JPanel timeplotPanel1, JPanel tp1, BasicVisualizationServer vv2,
			JPanel timeplotPanel2, JPanel tp2) {
		JPanel newPanel = new JPanel();
		nPanel++;

		if (nPanel % 2 == 1) {
			newPanel.setBackground(Color.GRAY);
		} else {
			newPanel.setBackground(Color.darkGray);
		}
		FlowLayout flow = new FlowLayout();
		newPanel.setLayout(flow);

		JPanel leftSide = new JPanel();
		leftSide.setLayout(new BoxLayout(leftSide, BoxLayout.Y_AXIS));
		leftSide.add(vv1);
		if (timeplotPanel1 != null) {
			leftSide.add(timeplotPanel1);
		}
		if (tp1 != null) {
			leftSide.add(tp1);
		}
		newPanel.add(leftSide);

		JPanel rightSide = new JPanel();
		rightSide.setLayout(new BoxLayout(rightSide, BoxLayout.Y_AXIS));
		rightSide.add(vv2);
		if (timeplotPanel2 != null) {
			rightSide.add(timeplotPanel2);
		}
		if (tp1 != null) {
			rightSide.add(tp2);
		}
		newPanel.add(rightSide);
		this.panel.add(newPanel);
	}

	class IndividualActionListioner implements ActionListener {
		Individual indiv;

		public IndividualActionListioner(Individual indiv) {
			this.indiv = indiv;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			JFrame frame = new JFrame("History for individual "
					+ indiv.generationIndex + "," + indiv.individualIndex);
			GraphDisplayer displayer = new GraphDisplayer(1300, 700);
			frame.add(displayer.getContentPanel());
			frame.setSize(1300, 700);
			frame.setVisible(true);
			Individual tmp = indiv;

			Stack<Individual> stack = new Stack<Individual>();
			do {
				stack.push(tmp);
				double parent1Fitness = (tmp.parent1 == null ? Double.MIN_VALUE
						: tmp.parent1.fitnessResult.finalResult());
				double parent2Fitness = (tmp.parent2 == null ? Double.MIN_VALUE
						: tmp.parent2.fitnessResult.finalResult());
				/*
				 * if (parent1Fitness > parent2Fitness) { if
				 * (tmp.parent1.generationIndex > -1){ for(int
				 * i=0;i<data.get(tmp.parent1.generationIndex).size();i++){
				 * Species sp = data.get(tmp.parent1.generationIndex).get(i);
				 * for(Individual blu : sp.individuals){ if(blu.individualIndex
				 * == tmp.parent1.individualIndex){ tmp = blu; i= 1000; //
				 * should break the exterior one too. TODO not safe break; } } }
				 * } } else { for(int
				 * i=0;i<data.get(tmp.parent2.generationIndex).size();i++){
				 * Species sp = data.get(tmp.parent2.generationIndex).get(i);
				 * for(Individual blu : sp.individuals){ if(blu.individualIndex
				 * == tmp.parent2.individualIndex){ tmp = blu; i= 1000; //
				 * should break the exterior one too. TODO not safe break; } } }
				 * }
				 */
				if (parent1Fitness > parent2Fitness) {
					tmp = tmp.parent1;
				} else {
					tmp = tmp.parent2;
				}
			} while (tmp != null);
			while (!stack.empty()) {
				Individual item = stack.pop();
				displayer.addGraph(
						item.getOligoModel().getVisualization(250, 250), null,
						item.printModelInTextArea());

			}
			displayer.display();
		}
	}

	class IndividualActionListionerVisual implements ActionListener {
		Individual indiv;

		public IndividualActionListionerVisual(Individual indiv) {
			this.indiv = indiv;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			JPanel panel = new JPanel();
			JFrame frame = new JFrame("History for Individual "
					+ indiv.generationIndex + "," + indiv.individualIndex);
			frame.add(panel);
			frame.setSize(1200, 800);
			PlotIndividualHistory demo = new PlotIndividualHistory("", indiv);
			panel.add(demo);
			frame.setVisible(true);
		}
	}

	class IndividualActionListionerText implements ActionListener {
		Individual indiv;

		public IndividualActionListionerText(Individual indiv) {
			this.indiv = indiv;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			PopupIndivInfo frame = new PopupIndivInfo(
					"Model Information for Individual " + indiv.generationIndex
							+ "," + indiv.individualIndex,
					indiv.getTextInformation());
			frame.setVisible(true);
		}
	}

	class IndividualActionListionerExport implements ActionListener {
		Individual indiv;

		public IndividualActionListionerExport(Individual indiv) {
			this.indiv = indiv;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			ExportEvolutionToImagesTool frame = new ExportEvolutionToImagesTool(
					"Export Evolution to Images for Individual "
							+ indiv.generationIndex + ","
							+ indiv.individualIndex, indiv);
			frame.setVisible(true);
		}
	}

	public void giveData(ArrayList<ArrayList<Species>> speciesData) {
		this.data = speciesData;
	}
}
